package com.xbongbong.lfasr.concurrent;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.jxc.concurrent.ConcurrentLock;
import com.xbongbong.lfasr.util.StringUtil;
import com.xbongbong.paas.constant.PaasConstant;
import com.xbongbong.paas.constant.TimeConstant;
import com.xbongbong.paas.domain.entity.PaasProcessDataEntity;
import com.xbongbong.paas.enums.DelEnum;
import com.xbongbong.paas.enums.TaskTypeEnum;
import com.xbongbong.paas.help.FormDataValidateProductHelp;
import com.xbongbong.paas.model.PaasProcessDataModel;
import com.xbongbong.paas.pojo.dto.ConcurrentLockDTO;
import com.xbongbong.paas.pojo.dto.FormDataAddDTO;
import com.xbongbong.paas.pojo.dto.RollBackStockDTO;
import com.xbongbong.paas.redis.PaasRedisHelper;
import com.xbongbong.paas.redis.RedisPrefixConstant;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.Arith;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.pro.domain.entity.FormConfigEntity;
import com.xbongbong.pro.enums.errorcodes.AssembleErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.InstockErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.InventoryErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.OutstockErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.ProductErrorCodeEnum;
import com.xbongbong.pro.enums.errorcodes.TransferErrorCodeEnum;
import com.xbongbong.pro.formdata.pojo.vo.BeforeSaveVO;
import com.xbongbong.pro.product.pojo.ProductStockLockPojo;
import com.xbongbong.saas.constant.BasicConstant;
import com.xbongbong.saas.constant.ParameterConstant;
import com.xbongbong.saas.constant.ProductConstant;
import com.xbongbong.saas.constant.StringConstant;
import com.xbongbong.saas.domain.entity.InstockProductEntity;
import com.xbongbong.saas.domain.entity.OutstockProductEntity;
import com.xbongbong.saas.domain.entity.ProductStockEntity;
import com.xbongbong.saas.domain.entity.ProductWarehouseEntity;
import com.xbongbong.saas.domain.entity.WarehouseOristockEntity;
import com.xbongbong.saas.domain.entity.ext.AssembleEntityExt;
import com.xbongbong.saas.domain.entity.ext.InstockEntityExt;
import com.xbongbong.saas.domain.entity.ext.OutstockEntityExt;
import com.xbongbong.saas.domain.entity.ext.ProductEntityExt;
import com.xbongbong.saas.enums.FormConfigEnum;
import com.xbongbong.saas.enums.HandleProductWayEnum;
import com.xbongbong.saas.enums.SelectProductEnum;
import com.xbongbong.saas.enums.XbbRefTypeEnum;
import com.xbongbong.saas.enums.business.AssembleEnum;
import com.xbongbong.saas.enums.business.InstockEnum;
import com.xbongbong.saas.enums.business.OutstockEnum;
import com.xbongbong.saas.enums.business.ProductEnum;
import com.xbongbong.saas.enums.product.InstockProductEnum;
import com.xbongbong.saas.enums.product.TransferProductEnum;
import com.xbongbong.saas.help.BatchKeyHelp;
import com.xbongbong.saas.help.SaasSaveHelp;
import com.xbongbong.saas.model.AssembleModel;
import com.xbongbong.saas.model.FormConfigModel;
import com.xbongbong.saas.model.InstockModel;
import com.xbongbong.saas.model.InventoryModel;
import com.xbongbong.saas.model.OutstockModel;
import com.xbongbong.saas.model.ProductModel;
import com.xbongbong.saas.model.ProductStockModel;
import com.xbongbong.saas.model.ProductWarehouseModel;
import com.xbongbong.saas.model.TransferModel;
import com.xbongbong.saas.model.WarehouseOristockModel;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.StringJoiner;

/**
 * @author WuHB
 * @date 2021/10/20 14:49
 */
//@Aspect
@Component
public class ConcurrentLockAspect {

    private static final Logger LOG = LoggerFactory.getLogger(ConcurrentLockAspect.class);

    @Resource
    private PaasRedisHelper paasRedisHelper;
    @Resource
    private InstockModel instockModel;
    @Resource
    private OutstockModel outstockModel;
    @Resource
    private AssembleModel assembleModel;
    @Resource
    private TransferModel transferModel;
    @Resource
    private InventoryModel inventoryModel;
    @Resource
    private WarehouseOristockModel warehouseOristockModel;
    @Resource
    private ProductStockModel productStockModel;
    @Resource
    private ProductWarehouseModel productWarehouseModel;
    @Resource
    private ProductRedisLock productRedisLock;
    @Resource
    private PaasProcessDataModel paasProcessDataModel;
    @Resource
    private FormDataValidateProductHelp formDataValidateProductHelp;
    @Resource
    private ProductModel productModel;
    @Resource
    private SaasSaveHelp saasSaveHelp;
    @Resource
    private FormConfigModel formConfigModel;

    /**
     * 环绕通知
     *
     * @param joinPoint joinPoint
     */
//    @Around("@annotation(concurrentLock)")
    public Object checkLock(ProceedingJoinPoint joinPoint, ConcurrentLock concurrentLock) throws Throwable {
        Object[] args = joinPoint.getArgs();
        String corpid = "";
        // 本次入库的所有产品ID集合
        List<String> productIdList = new ArrayList<>();
        Integer businessType = -1;
        // 操作的类型1新建 2删除
        HandleProductWayEnum handleProductWayEnum = concurrentLock.businessType();
        for (Object arg : args) {
            String jsonString = JSONObject.toJSONString(arg);
            ConcurrentLockDTO concurrentLockDTO = JSONObject.parseObject(jsonString, ConcurrentLockDTO.class);
            corpid = concurrentLockDTO.getCorpid();
            businessType = concurrentLockDTO.getBusinessType();
            XbbRefTypeEnum xbbRefTypeEnum = XbbRefTypeEnum.getByCode(businessType);
            JSONObject dataList = concurrentLockDTO.getDataList();
            JSONArray productArray = new JSONArray();
            // 新建删除需要的数据及数据处理方式不同
            // 如果不是影响成本的这些单据直接return放行
            if (!ProductConstant.EFFECT_PRODUCT_COST.contains(businessType)){
                return joinPoint.proceed();
            }
            if (Objects.equals(HandleProductWayEnum.ADD.getCode() , handleProductWayEnum.getCode())){
                handleAddData(xbbRefTypeEnum,dataList,productArray,productIdList,false);
                for (int i = 0; i < productArray.size(); i++) {
                    JSONObject jsonObject = productArray.getJSONObject(i);
                    Long productId = jsonObject.getJSONObject(InstockProductEnum.PRODUCT.getAttr()).getLong("dataId");
                    if (!productIdList.contains(productId)){
                        productIdList.add(productId.toString());
                    }
                }
            }else if (Objects.equals(HandleProductWayEnum.DELETE.getCode(),handleProductWayEnum.getCode())){
                // 删除单据的id集合
                List<Long> dataIdList = concurrentLockDTO.getDataIdList();
                handleDelData(corpid,xbbRefTypeEnum,dataIdList,productIdList,true);
            }
        }
//        synchronized (RedisPrefixConstant.PRODUCT_COST_LOCK + corpid){
        // 重试次数
        int retryNum = 0;
        // 最终要重新放入redis的产品ID集合
        do {
            retryNum ++;
            String value = paasRedisHelper.getMembers(RedisPrefixConstant.PRODUCT_COST_LOCK, corpid);
            LOG.info("获取到的公司产品锁为："+JSONObject.parseArray(value));
            if (!StringUtil.isEmpty(value)){
                List<String> productIds = JSONObject.parseArray(value, String.class);
                if (!productIds.retainAll(productIdList)){
                    // 并发时休眠三秒钟重试
                    if (retryNum <=3){
                        Thread.sleep(1000);
                    }else {
                        throw new XbbException(ProductErrorCodeEnum.API_ERROR_205114);
                    }
                }
            }else {
                break;
            }
        }while (retryNum <=3);
        if (CollectionsUtil.isNotEmpty(productIdList)){
            paasRedisHelper.addValue(RedisPrefixConstant.PRODUCT_COST_LOCK,corpid,productIdList,180);
        }
//        }

//        synchronized (RedisPrefixConstant.PRODUCT_COST_LOCK + corpid){
//            // 重试次数
//            int retryNum = 0;
//            // 最终要重新放入redis的产品ID集合
//            do {
//                retryNum ++;
//                String value = paasRedisHelper.getMembers(RedisPrefixConstant.PRODUCT_COST_LOCK, corpid);
//                if (!StringUtil.isEmpty(value)){
//                    List<String> productIds = JSONObject.parseArray(value, String.class);
//                    if (!productIds.retainAll(productIdList)){
//                        // 并发时休眠三秒钟重试
//                        if (retryNum <=3){
//                            Thread.sleep(1000);
//                        }else {
//                            throw new XbbException(ProductErrorCodeEnum.API_ERROR_205114);
//                        }
//                    }
//                }else {
//                    break;
//                }
//            }while (retryNum <=3);
//            if (CollectionsUtil.isNotEmpty(productIdList)){
//                paasRedisHelper.addValue(RedisPrefixConstant.PRODUCT_COST_LOCK,corpid,productIdList,180);
//            }
//        }
        return joinPoint.proceed();
    }

    /**
     * @Author: wujian
     * @Description: 前置通知 防止超发出库
     * @Date: 下午1:54 2021/12/7
     * @Param: []
     * @return: void
     **/
    @Before("@annotation(concurrentLock)")
    public void beforeMethod(JoinPoint joinPoint, ConcurrentLock concurrentLock) throws Throwable {
        Object[] args = joinPoint.getArgs();
        String corpid = "";
        // 本次入库的所有产品ID集合
        List<String> productIdWarehouseIdList = new ArrayList<>();
        Integer businessType = -1;
        // 操作的类型1新建 2删除
        HandleProductWayEnum handleProductWayEnum = concurrentLock.businessType();
        for (Object arg : args) {
            String jsonString = JSONObject.toJSONString(arg);
            ConcurrentLockDTO concurrentLockDTO = JSONObject.parseObject(jsonString, ConcurrentLockDTO.class);
            corpid = concurrentLockDTO.getCorpid();
            businessType = concurrentLockDTO.getBusinessType();
            XbbRefTypeEnum xbbRefTypeEnum = XbbRefTypeEnum.getByCode(businessType);
            JSONObject dataList = concurrentLockDTO.getDataList();
            JSONArray productArray = new JSONArray();
            // 新建删除需要的数据及数据处理方式不同
            // 如果不是影响成本的这些单据直接return放行
            if (!ProductConstant.EFFECT_PRODUCT_COST.contains(businessType)){
                return ;
            }
            Integer taskType = -1;
            // 本次新建的所有key和数量map
            Map<String,Double> needAddDataMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            if (Objects.equals(HandleProductWayEnum.ADD.getCode() , handleProductWayEnum.getCode()) || Objects.equals(HandleProductWayEnum.PROCESS.getCode(),handleProductWayEnum.getCode())){
                if (Objects.equals(HandleProductWayEnum.ADD.getCode(),handleProductWayEnum.getCode())){
                    productArray = dataList.getJSONArray(OutstockEnum.PRODUCT.getAttr());
                }else {
                    taskType = concurrentLockDTO.getTaskType();
                    Long processTaskId = concurrentLockDTO.getProcessTaskId();
                    // 根据taskId获取审批表中的数据
                    PaasProcessDataEntity processDataEntity = paasProcessDataModel.getByTaskId(processTaskId, corpid);
                    if (Objects.nonNull(processDataEntity) && Objects.nonNull(processDataEntity.getData())){
                        JSONObject data = JSON.parseObject(processDataEntity.getData());
                        productArray = data.getJSONArray(OutstockEnum.PRODUCT.getAttr());
                    }
                }
                List<ProductStockLockPojo> productStockLockPojos = handleAddRedisData(xbbRefTypeEnum, productArray, productIdWarehouseIdList, handleProductWayEnum.getCode() ,taskType);
                Map<String,Double> productNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                List<String> redisKeyList = new ArrayList<>();
                // 用于开启批次的去product_stock表中查询批次库存
                List<String> batchKeyList = new ArrayList<>();
                // 用于没开批次的去product_warehouse表中查询仓库库存
                Set<String> warehouseKeyList = new HashSet<>();
                if (CollectionsUtil.isNotEmpty(productStockLockPojos)){
                    for (ProductStockLockPojo pojo : productStockLockPojos) {
                        String redisKey = pojo.getRedisKey();
                        Double productNum = Objects.isNull(pojo.getProductNum()) ? 0D:pojo.getProductNum();
                        productNumMap.put(redisKey,productNum);
                        redisKeyList.add(redisKey);
                        // 是否开启批次
                        boolean enableBatch = pojo.isEnableBatch();
                        // redis中不包含的要从数据库中查
//                        if (!redisDataMap.containsKey(redisKey)){
                        if (enableBatch){
                            batchKeyList.add(pojo.getBatchKey());
                        }else {
                            warehouseKeyList.add(pojo.getWarehouseKey());
                        }
//                        }
                    }
                    // 将本次需要添加到redis中的数据通过lua脚本插入redis
                    // key + 本次出入库数量 + 数据库数量
                    List<String> needAddDataList = new ArrayList<>();
                    // 封装批次库存数量
                    Map<String,Double> batchMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    // 封装没批次库存数量
                    Map<String,Double> productWarehouseStockMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    Map<String,Object> param = new HashMap<>();
                    param.put("corpid",corpid);
                    param.put("del",DelEnum.NORMAL.getDel());
                    param.put("batchKeyIn", batchKeyList);
                    param.put("warehouseChecked",1);
                    switch (xbbRefTypeEnum){
                        case OUTSTOCK:
                        case CONTRACT_OUTSTOCK:
                        case RETURNED_PURCHASE_OUTSTOCK:
                        case OTHER_OUTSTOCK:
                        case PRODUCTION_MATERIEL_OUTSTOCK:
                        case WORK_ORDER_OUTSTOCK:
                            if (CollectionsUtil.isNotEmpty(batchKeyList)){
                                param.put("batchKeyIn", batchKeyList);
                                List<ProductStockEntity> warehouseBatchStockList = productStockModel.findEntitys(param);
                                param.remove("batchKeyIn");
                                if (CollectionsUtil.isNotEmpty(warehouseBatchStockList)){
                                    warehouseBatchStockList.forEach(item -> {
                                        // productId + "_" + warehouseId + "_" + batch + 生产日期 + 保质期
                                        batchMap.put(item.getProductId() + "_" + item.getWarehouseId() + "_" + item.getBatch() + "_" + item.getProduceDate() + "_" + item.getGuaranteePeriod() ,item.getNum());
                                    });
                                }
                            }

                            if (CollectionsUtil.isNotEmpty(warehouseKeyList)){
                                param.put("warehouseKeyIn", warehouseKeyList);
                                List<ProductWarehouseEntity> productWarehouseList = productWarehouseModel.findEntitysWithWarehouseChecked(param, 1);
                                if (CollectionsUtil.isNotEmpty(productWarehouseList)){
                                    productWarehouseList.forEach(item -> productWarehouseStockMap.put(item.getWarehouseKey() + "_" + "",item.getNum()));
                                }
                            }
                            break;
                        case OTHER_INSTOCK:
                        case PURCHASE_INSTOCK:
                        case REFUND_INSTOCK:
                        case PRODUCTION_INSTOCK:
                        case RETURNED_MATERIEL_INSTOCK:
                            // 新建入库单 数据库没查到的 默认塞0
                            if (CollectionsUtil.isNotEmpty(batchKeyList)){
                                param.put("batchKeyIn", batchKeyList);
                                List<ProductStockEntity> warehouseBatchStockList = productStockModel.findEntitys(param);
                                param.remove("batchKeyIn");
                                if (CollectionsUtil.isNotEmpty(warehouseBatchStockList)){
                                    warehouseBatchStockList.forEach(item -> {
                                        // productId + "_" + warehouseId + "_" + batch + 生产日期 + 保质期
                                        batchMap.put(item.getBatchKey() ,item.getNum());
                                    });
                                }
                                // 循环batchKeyList  查处的批次数据没有的时候默认塞0进去
                                for (String batchKey : batchKeyList) {
                                    if (!batchMap.containsKey(batchKey)){
                                        batchMap.put(batchKey,0D);
                                    }
                                }
                            }
                            if (CollectionsUtil.isNotEmpty(warehouseKeyList)){
                                param.put("warehouseKeyIn", warehouseKeyList);
                                List<ProductWarehouseEntity> productWarehouseList = productWarehouseModel.findEntitysWithWarehouseChecked(param, 1);
                                if (CollectionsUtil.isNotEmpty(productWarehouseList)){
                                    productWarehouseList.forEach(item -> productWarehouseStockMap.put(item.getWarehouseKey() + "_" + "",item.getNum()));
                                }
                                for (String warehouseKey : warehouseKeyList) {
                                    if (!productWarehouseStockMap.containsKey(warehouseKey)){
                                        productWarehouseStockMap.put(warehouseKey + "_" + "",0D);
                                    }
                                }
                            }
                            break;
                    }
                    // 将数据库中查询出的数据放入redis查询出的map中 用lua脚本处理
                    if (!batchMap.isEmpty()){
                        batchMap.keySet().forEach(item -> needAddDataMap.put(item,batchMap.get(item)));
                    }
                    if (!productWarehouseStockMap.isEmpty()){
                        productWarehouseStockMap.keySet().forEach(item ->needAddDataMap.put(item,productWarehouseStockMap.get(item)));
                    }
                    if (!needAddDataMap.isEmpty()){
                        for (Map.Entry<String, Double> stringDoubleEntry : needAddDataMap.entrySet()) {
                            String key = stringDoubleEntry.getKey();
                            Double val = stringDoubleEntry.getValue();
                            Double num = productNumMap.getOrDefault(key,0D);
                            needAddDataList.add(key + "{test}"  + "ø/ø" + num + "ø/ø" + val + "ø/ø" );
                        }
                    }
                    boolean isSuccess = productRedisLock.handleSingleProductStock(RedisPrefixConstant.PRODUCT_STOCK_LOCK,redisKeyList, needAddDataList);
                    LOG.info("key为:" + needAddDataList.toString() + "是否通过：" + isSuccess);
                    if (isSuccess){
                        return ;
                    }else {
                        throw new XbbException(OutstockErrorCodeEnum.API_ERROR_212010);
                    }
                }

            }else if (Objects.equals(HandleProductWayEnum.DELETE.getCode(),handleProductWayEnum.getCode())){

            }else if (Objects.equals(HandleProductWayEnum.PROCESS.getCode(),handleProductWayEnum.getCode())){
                // 处理审批中更改数量的单据(销售出库 采购入库)
                if (Objects.equals(XbbRefTypeEnum.CONTRACT_OUTSTOCK.getCode(),xbbRefTypeEnum) || Objects.equals(XbbRefTypeEnum.PURCHASE_INSTOCK.getCode(),xbbRefTypeEnum)){
                    taskType = concurrentLockDTO.getTaskType();
                    Long processTaskId = concurrentLockDTO.getProcessTaskId();
                    // 根据taskId获取审批表中的数据
                    if (Objects.nonNull(processTaskId)){
                        PaasProcessDataEntity processDataEntity = paasProcessDataModel.getByTaskId(processTaskId, corpid);
                        JSONObject data = JSON.parseObject(processDataEntity.getData());
                        productArray = data.getJSONArray(OutstockEnum.PRODUCT.getAttr());

                    }

                    if (Objects.equals(TaskTypeEnum.COMMIT.getType(),taskType)){
                        // 提交时根据单据类型直接做redis中的数量加减

                    }else if (Objects.equals(TaskTypeEnum.STORAGE.getType(),taskType)){
                        // 暂存能该数量 所以也要用redis中的值加上修改的数量前后的差值


                    }else if (Objects.equals(TaskTypeEnum.REVOKE.getType(),taskType)){
                        // 撤回时根据单据类型回退数量


                    }else if (Objects.equals(TaskTypeEnum.AGAIN_COMMIT.getType(),taskType)){
                        // 重新提交跟第一次提交时一样 修改redis中的数量


                    }else if (Objects.equals(TaskTypeEnum.END.getType(),taskType)){
                        // 拒绝时根据业务类型 回退数量

                    }
                }
            }

        }
        return ;
    }


    public List<ProductStockLockPojo> handleAddRedisData(XbbRefTypeEnum xbbRefTypeEnum,JSONArray productArray, List<String> productIdWarehouseList,Integer handProduct,Integer taskType){
        // TODO不同单据的数据处理
        List<ProductStockLockPojo> ProductStockLockPojoList = new ArrayList<>();
        for (int i = 0; i < productArray.size(); i++) {
            ProductStockLockPojo pojo = new ProductStockLockPojo();
            Long productId = -1L;
            Long warehouseId = -1L;
            JSONObject jsonObject = productArray.getJSONObject(i);
            productId = jsonObject.getJSONObject(InstockProductEnum.PRODUCT.getAttr()).getLong("id");
            warehouseId = jsonObject.getJSONArray(InstockProductEnum.WAREHOUSE.getAttr()).getJSONObject(0).getLong("id");
            pojo.setProductId(productId);
            pojo.setWarehouseId(warehouseId);
            Integer enableBatch= jsonObject.getInteger(ProductConstant.Enable_Batch_Shelf_Life);
            String batch = "";
            pojo.setEnableBatch(false);
            Long productDate = 0L;
            Long guaranteePeriod = 0L;
            // 开启了批次的要拼接上批次
            if (Objects.equals(BasicConstant.ONE,enableBatch)){
                pojo.setEnableBatch(true);
                // 到期日期
                productDate = jsonObject.getLong(InstockProductEnum.PRODUCE_DATE.getAttr());
                // 保质期
                guaranteePeriod = jsonObject.getLong(InstockProductEnum.GUARANTEE_PERIOD.getAttr());
                if(Objects.nonNull(guaranteePeriod)){
                    // 保质期传参为天 数据库中存储的为时间戳
                    guaranteePeriod = guaranteePeriod * 86400;
                }

                pojo.setProductDate(productDate);
                pojo.setGuaranteePeriod(guaranteePeriod);
            }
            String warehouseKey = productId + "_" + warehouseId;
            pojo.setWarehouseKey(warehouseKey);

            Integer enableMultiUnit = jsonObject.getInteger(ProductEnum.ENABLE_MULTI_UNIT.getSaasAttr());
            Double productNum = jsonObject.getDouble(InstockProductEnum.NUM.getAttr());
            pojo.setProductNum(productNum);
            if (Objects.equals(BasicConstant.ONE,enableMultiUnit)){
                JSONObject unitObject = jsonObject.getJSONObject(InstockProductEnum.UNIT.getAttr());
                Long businessUnit = unitObject.getLong("value");
                JSONArray unitArray = jsonObject.getJSONArray(ProductConstant.TRANSFORM_UNIT_RATE);
                for (int j = 0; j < unitArray.size(); j++) {
                    JSONObject unitPojo = unitArray.getJSONObject(j);
                    Long value = unitPojo.getLong("value");
                    if (Objects.equals(businessUnit,value)){
                        Double rate = unitPojo.getDouble("rate");
                        productNum = Arith.mul(productNum,rate);
                        pojo.setProductNum(productNum);
                        break;
                    }
                }
            }
            switch (xbbRefTypeEnum){
                case OUTSTOCK:
                case CONTRACT_OUTSTOCK:
                case RETURNED_PURCHASE_OUTSTOCK:
                case OTHER_OUTSTOCK:
                case PRODUCTION_MATERIEL_OUTSTOCK:
                case WORK_ORDER_OUTSTOCK:
                    pojo.setProductNum(productNum);
                    if (Objects.equals(BasicConstant.ONE,enableBatch)){
                        batch = jsonObject.getJSONObject(InstockProductEnum.BATCH.getAttr()).getString("value");
                    }
                    break;
                case OTHER_INSTOCK:
                case PURCHASE_INSTOCK:
                case REFUND_INSTOCK:
                case PRODUCTION_INSTOCK:
                case RETURNED_MATERIEL_INSTOCK:
                    if (Objects.equals(BasicConstant.ONE,enableBatch)){
                        batch = jsonObject.getString(InstockProductEnum.BATCH.getAttr());
                    }
                    pojo.setProductNum(-productNum);
                    break;
            }
            String batchKey = BatchKeyHelp.getBatchKey(productId, warehouseId, batch, productDate, guaranteePeriod);
            pojo.setBatchKey(batchKey);
            pojo.setBatch(batch);
            String productWarehouseKey = productId + "_" + warehouseId + "_" + batch + "_" +productDate  + "_" + guaranteePeriod;
            productIdWarehouseList.add(productWarehouseKey);
            pojo.setRedisKey(productWarehouseKey);
            ProductStockLockPojoList.add(pojo);
        }

        return ProductStockLockPojoList;
    }
    public void handleAddData(XbbRefTypeEnum xbbRefTypeEnum,JSONObject dataList,JSONArray productArray,List<String> productIdList,boolean isAfter){
        switch (xbbRefTypeEnum){
            case TRANSFER:
            case INVENTORY:
            case ORI_STOCK:
            case OTHER_INSTOCK:
            case PURCHASE_INSTOCK:
            case RETURNED_MATERIEL_INSTOCK:
            case PRODUCTION_INSTOCK:
            case REFUND_INSTOCK:
            case CONTRACT_OUTSTOCK:
            case ORDER_OUTSTOCK:
            case OTHER_OUTSTOCK:
            case RETURNED_PURCHASE_OUTSTOCK:
            case PRODUCTION_MATERIEL_OUTSTOCK:
            case WORK_ORDER_OUTSTOCK:
                productArray = dataList.getJSONArray(OutstockEnum.PRODUCT.getAttr());
                for (int i = 0; i < productArray.size(); i++) {
                    Long productId = -1L;
                    JSONObject jsonObject = productArray.getJSONObject(i);
                    if (isAfter){
                        productId = jsonObject.getLong(InstockProductEnum.PRODUCT.getAttr());
                        Long refProductId = jsonObject.getLong("refProductId");
                        if ( Objects.nonNull(refProductId) ) {
                            productIdList.add(String.valueOf(refProductId));
                        }
                    }else {
                        productId = jsonObject.getJSONObject(InstockProductEnum.PRODUCT.getAttr()).getLong("id");
                    }
                    productIdList.add(String.valueOf(productId));
                }
                break;
            case ASSEMBLE:
                // 装配单特殊处理
                productArray = dataList.getJSONArray(AssembleEnum.OUT_PRODUCTS.getAttr());
                productArray.addAll(dataList.getJSONArray(AssembleEnum.IN_PRODUCTS.getAttr()));
                break;
            default:
                // 不是这些出入库类型的直接放行
                return ;
        }
    }

    public List<String> handleDelData(String corpid,XbbRefTypeEnum xbbRefTypeEnum,List<Long> dataIdList,List<String> productIdList,boolean isBefore){
        Map<String, Object> param = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        param.put(StringConstant.CORPID, corpid);
        param.put("idIn", dataIdList);
        // 因为盘点调拨单删除时需要先删除对应的其他出入库单所以不用处理
        switch (xbbRefTypeEnum){
            case OTHER_INSTOCK:
            case PURCHASE_INSTOCK:
            case RETURNED_MATERIEL_INSTOCK:
            case PRODUCTION_INSTOCK:
            case REFUND_INSTOCK:
                List<InstockEntityExt> entitys = instockModel.findEntitys(param);
                if (CollectionsUtil.isNotEmpty(entitys)){
                    for (InstockEntityExt entity : entitys) {
                        JSONObject data = entity.getData();
                        JSONArray productIds = data.getJSONArray(InstockEnum.PRODUCT.getAttr());
                        productIdList.addAll(productIds.toJavaList(String.class));
                    }
                }
                break;
            case CONTRACT_OUTSTOCK:
            case ORDER_OUTSTOCK:
            case OTHER_OUTSTOCK:
            case RETURNED_PURCHASE_OUTSTOCK:
            case PRODUCTION_MATERIEL_OUTSTOCK:
            case WORK_ORDER_OUTSTOCK:
                List<OutstockEntityExt> outstockEntityExtList = outstockModel.findEntitys(param);
                if (CollectionsUtil.isNotEmpty(outstockEntityExtList)){
                    for (OutstockEntityExt entity : outstockEntityExtList) {
                        JSONObject data = entity.getData();
                        JSONArray productIds = data.getJSONArray(InstockEnum.PRODUCT.getAttr());
                        productIdList.addAll(productIds.toJavaList(String.class));
                    }
                }
                break;
            case ORI_STOCK:
                Map<String, Object> oriParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                oriParam.put(StringConstant.CORPID, corpid);
                oriParam.put("warehouseId", dataIdList.get(0));
                if (isBefore){
                    oriParam.put("del",DelEnum.NORMAL.getDel());
                }
                List<WarehouseOristockEntity> oristockModelEntitys = warehouseOristockModel.findEntitys(oriParam);
                if (CollectionsUtil.isNotEmpty(oristockModelEntitys)){
                    for (WarehouseOristockEntity entity : oristockModelEntitys) {
                        JSONObject data = entity.getData();
                        JSONArray productDataArray = data.getJSONArray(InstockEnum.PRODUCT.getAttr());
                        for (int i = 0; i < productDataArray.size(); i++) {
                            JSONObject jsonObject = productDataArray.getJSONObject(i);
                            Object productId = jsonObject.get(SelectProductEnum.PRODUCT.getAttr());
                            productIdList.add(productId.toString());
                        }
                    }
                }
                break;
            case ASSEMBLE:
                // 因为装配单删除时是只传了个装配单ID 所以要根据装配单ID查询出所有装配单 再拿到所有装配单下的装配出入库单
                Map<String, Object> assembleParam = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                assembleParam.put(StringConstant.CORPID, corpid);
                assembleParam.put("idIn", dataIdList);
                List<AssembleEntityExt> assembleModelEntitys = assembleModel.findEntitys(assembleParam);
                // 装配单先拿到装配出入库单
                if (CollectionsUtil.isNotEmpty(assembleModelEntitys)){
                    for (AssembleEntityExt assembleModelEntity : assembleModelEntitys) {
                        JSONObject data = assembleModelEntity.getData();
                        JSONArray instockProductIdList = data.getJSONArray(AssembleEnum.IN_PRODUCTS.getAttr());
                        if (CollectionsUtil.isNotEmpty(instockProductIdList)){
                            productIdList.addAll(instockProductIdList.toJavaList(String.class));
                        }
                        JSONArray outstockProductIdList = data.getJSONArray(AssembleEnum.OUT_PRODUCTS.getAttr());
                        if (CollectionsUtil.isNotEmpty(outstockProductIdList)){
                            productIdList.addAll(outstockProductIdList.toJavaList(String.class));
                        }
                    }
                }
                break;
        }
        return productIdList;
    }

    // 在beforeSave方法执行完的后置通知 过滤掉审批的数据
    @AfterReturning(value = "@annotation(concurrentLock)", returning = "obj")
    public void afterMethod(JoinPoint joinPoint, ConcurrentLock concurrentLock,Object obj) throws Throwable{
        Object[] args = joinPoint.getArgs();
        BeforeSaveVO beforeSaveVO = (BeforeSaveVO)obj;
        Integer noApprovalRequired = beforeSaveVO.getNoApprovalRequired();
        // 进审批的直接返回 1不进审批  0 进审批
        if (Objects.equals(BasicConstant.ZERO,noApprovalRequired)){
            return;
        }
        String corpid = "";
        // 本次入库的所有产品ID集合
        List<String> productIdWarehouseIdList = new ArrayList<>();
        Integer businessType = -1;
        // 操作的类型1新建 2删除
        HandleProductWayEnum handleProductWayEnum = concurrentLock.businessType();
        for (Object arg : args) {
            String jsonString = JSONObject.toJSONString(arg);
            FormDataAddDTO concurrentLockDTO = JSONObject.parseObject(jsonString, FormDataAddDTO.class);
            // 是否符合触发工作流
            Boolean isSatisfyTrigger = concurrentLockDTO.getIsSatisfyTrigger();
            // 不进工作流的才处理
            if (isSatisfyTrigger){
                return;
            }
            corpid = concurrentLockDTO.getCorpid();
            businessType = concurrentLockDTO.getBusinessType();
            XbbRefTypeEnum xbbRefTypeEnum = XbbRefTypeEnum.getByCode(businessType);
            JSONObject dataList = concurrentLockDTO.getDataList();
            JSONArray productArray = new JSONArray();
            // 新建删除需要的数据及数据处理方式不同
            // 如果不是影响成本的这些单据直接return放行
            if (!ProductConstant.EFFECT_PRODUCT_COST.contains(businessType)){
                return ;
            }
            Integer taskType = -1;
            // 本次新建的所有key和数量map
            Map<String,Double> needAddDataMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            if (Objects.equals(HandleProductWayEnum.ADD.getCode() , handleProductWayEnum.getCode())){
                productArray = dataList.getJSONArray(OutstockEnum.PRODUCT.getAttr());
                List<ProductStockLockPojo> productStockLockPojos = handleAfterAddRedisData(xbbRefTypeEnum, productArray, productIdWarehouseIdList);
                Map<String,Double> productNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                List<String> redisKeyList = new ArrayList<>();
                // 用于开启批次的去product_stock表中查询批次库存
                List<String> batchKeyList = new ArrayList<>();
                // 用于没开批次的去product_warehouse表中查询仓库库存
                Set<String> warehouseKeyList = new HashSet<>();
                if (CollectionsUtil.isNotEmpty(productStockLockPojos)){
                    for (ProductStockLockPojo pojo : productStockLockPojos) {
                        String redisKey = pojo.getRedisKey();
                        Double productNum = Objects.isNull(pojo.getProductNum()) ? 0D:pojo.getProductNum();
                        productNumMap.put(redisKey,productNum);
                        redisKeyList.add(redisKey);
                        // 是否开启批次
                        boolean enableBatch = pojo.isEnableBatch();
                        if (enableBatch){
                            batchKeyList.add(pojo.getBatchKey());
                        }else {
                            warehouseKeyList.add(pojo.getWarehouseKey());
                        }
                    }
                    // 将本次需要添加到redis中的数据通过lua脚本插入redis
                    // key + 本次出入库数量 + 数据库数量
                    List<String> needAddDataList = new ArrayList<>();
                    // 封装批次库存数量
                    Map<String,Double> batchMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    // 封装没批次库存数量
                    Map<String,Double> productWarehouseStockMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
                    Map<String,Object> param = new HashMap<>();
                    param.put("corpid",corpid);
                    param.put("del",DelEnum.NORMAL.getDel());
                    param.put("batchKeyIn", batchKeyList);
                    param.put("warehouseChecked",1);
                    switch (xbbRefTypeEnum){
                        case OUTSTOCK:
                        case CONTRACT_OUTSTOCK:
                        case RETURNED_PURCHASE_OUTSTOCK:
                        case OTHER_OUTSTOCK:
                        case PRODUCTION_MATERIEL_OUTSTOCK:
                        case WORK_ORDER_OUTSTOCK:
                            if (CollectionsUtil.isNotEmpty(batchKeyList)){
                                param.put("batchKeyIn", batchKeyList);
                                List<ProductStockEntity> warehouseBatchStockList = productStockModel.findEntitys(param);
                                param.remove("batchKeyIn");
                                if (CollectionsUtil.isNotEmpty(warehouseBatchStockList)){
                                    warehouseBatchStockList.forEach(item -> {
                                        // productId + "_" + warehouseId + "_" + batch + 生产日期 + 保质期
                                        batchMap.put(item.getProductId() + "_" + item.getWarehouseId() + "_" + item.getBatch() + "_" + item.getProduceDate() + "_" + item.getGuaranteePeriod() ,item.getNum());
                                    });
                                }
                            }

                            if (CollectionsUtil.isNotEmpty(warehouseKeyList)){
                                param.put("warehouseKeyIn", warehouseKeyList);
                                List<ProductWarehouseEntity> productWarehouseList = productWarehouseModel.findEntitysWithWarehouseChecked(param, 1);
                                if (CollectionsUtil.isNotEmpty(productWarehouseList)){
                                    productWarehouseList.forEach(item -> productWarehouseStockMap.put(item.getWarehouseKey() + "_" + "",item.getNum()));
                                }
                            }
                            break;
                        case OTHER_INSTOCK:
                        case PURCHASE_INSTOCK:
                        case REFUND_INSTOCK:
                        case PRODUCTION_INSTOCK:
                        case RETURNED_MATERIEL_INSTOCK:
                            // 新建入库单 数据库没查到的 默认塞0
                            if (CollectionsUtil.isNotEmpty(batchKeyList)){
                                param.put("batchKeyIn", batchKeyList);
                                List<ProductStockEntity> warehouseBatchStockList = productStockModel.findEntitys(param);
                                param.remove("batchKeyIn");
                                if (CollectionsUtil.isNotEmpty(warehouseBatchStockList)){
                                    warehouseBatchStockList.forEach(item -> {
                                        // productId + "_" + warehouseId + "_" + batch + 生产日期 + 保质期
                                        batchMap.put(item.getBatchKey() ,item.getNum());
                                    });
                                }
                                // 循环batchKeyList  查处的批次数据没有的时候默认塞0进去
                                for (String batchKey : batchKeyList) {
                                    if (!batchMap.containsKey(batchKey)){
                                        batchMap.put(batchKey,0D);
                                    }
                                }
                            }
                            if (CollectionsUtil.isNotEmpty(warehouseKeyList)){
                                param.put("warehouseKeyIn", warehouseKeyList);
                                List<ProductWarehouseEntity> productWarehouseList = productWarehouseModel.findEntitysWithWarehouseChecked(param, 1);
                                if (CollectionsUtil.isNotEmpty(productWarehouseList)){
                                    productWarehouseList.forEach(item -> productWarehouseStockMap.put(item.getWarehouseKey() + "_" + "",item.getNum()));
                                }
                                for (String warehouseKey : warehouseKeyList) {
                                    if (!productWarehouseStockMap.containsKey(warehouseKey)){
                                        productWarehouseStockMap.put(warehouseKey + "_" + "",0D);
                                    }
                                }
                            }
                            break;
                    }
                    // 将数据库中查询出的数据放入redis查询出的map中 用lua脚本处理
                    if (!batchMap.isEmpty()){
                        batchMap.keySet().forEach(item -> needAddDataMap.put(item,batchMap.get(item)));
                    }
                    if (!productWarehouseStockMap.isEmpty()){
                        productWarehouseStockMap.keySet().forEach(item ->needAddDataMap.put(item,productWarehouseStockMap.get(item)));
                    }
                    if (!needAddDataMap.isEmpty()){
                        for (Map.Entry<String, Double> stringDoubleEntry : needAddDataMap.entrySet()) {
                            String key = stringDoubleEntry.getKey();
                            Double val = stringDoubleEntry.getValue();
                            Double num = productNumMap.getOrDefault(key,0D);
                            needAddDataList.add(key + "ø/ø" + num + "ø/ø" + val + "ø/ø" );
                        }
                    }
                    boolean isSuccess = productRedisLock.handleSingleProductStock(RedisPrefixConstant.PRODUCT_STOCK_LOCK,redisKeyList, needAddDataList);
                    LOG.info("key为:" + needAddDataList.toString() + "是否通过：" + isSuccess);
                    if (isSuccess){
                        return ;
                    }else {
                        throw new XbbException(OutstockErrorCodeEnum.API_ERROR_212010);
                    }
                }
            }else if (Objects.equals(HandleProductWayEnum.DELETE.getCode(),handleProductWayEnum.getCode())){

            }
        }

        return ;
    }


    /**
     * @Author: wujian
     * @Description: 用在beforeSave方法之后的后置通知处理数据
     * @Date: 下午10:49 2021/12/14
     * @Param: [xbbRefTypeEnum, productArray, productIdWarehouseList, handProduct, taskType]
     * @return: java.util.List<com.xbongbong.pro.product.pojo.ProductStockLockPojo>
     **/
    public List<ProductStockLockPojo> handleAfterAddRedisData(XbbRefTypeEnum xbbRefTypeEnum,JSONArray productArray, List<String> productIdWarehouseList){
        // TODO不同单据的数据处理
        List<ProductStockLockPojo> ProductStockLockPojoList = new ArrayList<>();
        for (int i = 0; i < productArray.size(); i++) {
            ProductStockLockPojo pojo = new ProductStockLockPojo();
            Long productId = -1L;
            Long warehouseId = -1L;
            JSONObject jsonObject = productArray.getJSONObject(i);
            productId = jsonObject.getLong(InstockProductEnum.PRODUCT.getAttr());
            Object wareObj = jsonObject.get(InstockProductEnum.WAREHOUSE.getAttr());
            if (wareObj instanceof Number || wareObj instanceof String) {
                warehouseId = Long.parseLong(jsonObject.getString(InstockProductEnum.WAREHOUSE.getAttr()));
            }else {
                warehouseId = jsonObject.getJSONArray(InstockProductEnum.WAREHOUSE.getAttr()).getJSONObject(0).getLong("id");
            }

            pojo.setProductId(productId);
            pojo.setWarehouseId(warehouseId);
            Integer enableBatch= jsonObject.getInteger(ProductConstant.Enable_Batch_Shelf_Life);
            String batch = "";
            pojo.setEnableBatch(false);
            Long productDate = 0L;
            Long guaranteePeriod = 0L;
            // 开启了批次的要拼接上批次
            if (Objects.equals(BasicConstant.ONE,enableBatch)){
                pojo.setEnableBatch(true);
                // 到期日期
                productDate = jsonObject.getLong(InstockProductEnum.PRODUCE_DATE.getAttr());
                // 保质期
                guaranteePeriod = jsonObject.getLong(InstockProductEnum.GUARANTEE_PERIOD.getAttr());
                if(Objects.nonNull(guaranteePeriod)){
                    // 保质期传参为天 数据库中存储的为时间戳
                    guaranteePeriod = guaranteePeriod * 86400;
                }

                pojo.setProductDate(productDate);
                pojo.setGuaranteePeriod(guaranteePeriod);
            }
            String warehouseKey = productId + "_" + warehouseId;
            pojo.setWarehouseKey(warehouseKey);

            Integer enableMultiUnit = jsonObject.getInteger(ProductEnum.ENABLE_MULTI_UNIT.getSaasAttr());
            Double productNum = jsonObject.getDouble(InstockProductEnum.NUM.getAttr());
            pojo.setProductNum(productNum);
            if (Objects.equals(BasicConstant.ONE,enableMultiUnit)){
//                JSONObject unitObject = jsonObject.getJSONObject(InstockProductEnum.UNIT.getAttr());
                Long businessUnit = jsonObject.getLong(InstockProductEnum.UNIT.getAttr());
                JSONArray unitArray = jsonObject.getJSONArray(ProductConstant.TRANSFORM_UNIT_RATE);
                for (int j = 0; j < unitArray.size(); j++) {
                    JSONObject unitPojo = unitArray.getJSONObject(j);
                    Long value = unitPojo.getLong("value");
                    if (Objects.equals(businessUnit,value)){
                        Double rate = unitPojo.getDouble("rate");
                        productNum = Arith.mul(productNum,rate);
                        pojo.setProductNum(productNum);
                        break;
                    }
                }
            }
            switch (xbbRefTypeEnum){
                case OUTSTOCK:
                case CONTRACT_OUTSTOCK:
                case RETURNED_PURCHASE_OUTSTOCK:
                case OTHER_OUTSTOCK:
                case PRODUCTION_MATERIEL_OUTSTOCK:
                case WORK_ORDER_OUTSTOCK:
                    pojo.setProductNum(productNum);
                    if (Objects.equals(BasicConstant.ONE,enableBatch)){
                        batch = jsonObject.getString(InstockProductEnum.BATCH.getAttr());
                    }
                    break;
                case OTHER_INSTOCK:
                case PURCHASE_INSTOCK:
                case REFUND_INSTOCK:
                case PRODUCTION_INSTOCK:
                case RETURNED_MATERIEL_INSTOCK:
                    if (Objects.equals(BasicConstant.ONE,enableBatch)){
                        batch = jsonObject.getString(InstockProductEnum.BATCH.getAttr());
                    }
                    pojo.setProductNum(-productNum);
                    break;
            }
            String batchKey = BatchKeyHelp.getBatchKey(productId, warehouseId, batch, productDate, guaranteePeriod);
            pojo.setBatchKey(batchKey);
            pojo.setBatch(batch);
            String productWarehouseKey = BatchKeyHelp.getBatchKey(productId , warehouseId , batch ,productDate  , guaranteePeriod);
            productIdWarehouseList.add(productWarehouseKey);
            pojo.setRedisKey(productWarehouseKey);
            ProductStockLockPojoList.add(pojo);
        }

        return ProductStockLockPojoList;
    }

    // 超发出库校验
    public void surpassOutstock(FormDataAddDTO concurrentLockDTO, BeforeSaveVO beforeSaveVO) throws XbbException {
        String corpid = concurrentLockDTO.getCorpid();
        Integer businessType = concurrentLockDTO.getBusinessType();
        XbbRefTypeEnum xbbRefTypeEnum = XbbRefTypeEnum.getByCode(businessType);
        // 本次入库的所有产品ID集合
        List<String> productIdWarehouseIdList = new ArrayList<>();
        JSONObject dataList = concurrentLockDTO.getDataList();
        JSONArray productArray = new JSONArray();
        // 新建删除需要的数据及数据处理方式不同
        // 如果不是影响成本的这些单据直接return放行
        if (!ProductConstant.EFFECT_PRODUCT_COST.contains(businessType)){
            throw new XbbException(OutstockErrorCodeEnum.API_ERROR_212010);
        }
        Integer taskType = -1;
        // 本次新建的所有key和数量map
        Map<String,Double> needAddDataMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        productArray = dataList.getJSONArray(OutstockEnum.PRODUCT.getAttr());
        List<ProductStockLockPojo> productStockLockPojos = handleAfterAddRedisData(xbbRefTypeEnum, productArray, productIdWarehouseIdList);
        Map<String,Double> productNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<String> redisKeyList = new ArrayList<>();
        // 用于开启批次的去product_stock表中查询批次库存
        List<String> batchKeyList = new ArrayList<>();
        // 用于没开批次的去product_warehouse表中查询仓库库存
        Set<String> warehouseKeyList = new HashSet<>();
        if (CollectionsUtil.isNotEmpty(productStockLockPojos)){
            for (ProductStockLockPojo pojo : productStockLockPojos) {
                String redisKey = pojo.getRedisKey();
                Double productNum = Objects.isNull(pojo.getProductNum()) ? 0D:pojo.getProductNum();
                if (productNumMap.containsKey(redisKey)){
                    productNumMap.put(redisKey,Arith.add(productNumMap.get(redisKey),productNum));
                }else {
                    productNumMap.put(redisKey,productNum);
                }
                redisKeyList.add(redisKey);
                // 是否开启批次
                boolean enableBatch = pojo.isEnableBatch();
                if (enableBatch){
                    batchKeyList.add(pojo.getBatchKey());
                }else {
                    warehouseKeyList.add(pojo.getWarehouseKey());
                }
            }
            // 将本次需要添加到redis中的数据通过lua脚本插入redis
            // key + 本次出入库数量 + 数据库数量
            List<String> needAddDataList = new ArrayList<>();
            // 封装批次库存数量
            Map<String,Double> batchMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            // 封装没批次库存数量
            Map<String,Double> productWarehouseStockMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<String,Object> param = new HashMap<>();
            param.put("corpid",corpid);
            param.put("del",DelEnum.NORMAL.getDel());
            param.put("warehouseChecked",1);
            switch (xbbRefTypeEnum){
                case OUTSTOCK:
                case CONTRACT_OUTSTOCK:
                case RETURNED_PURCHASE_OUTSTOCK:
                case OTHER_OUTSTOCK:
                case PRODUCTION_MATERIEL_OUTSTOCK:
                case WORK_ORDER_OUTSTOCK:
                    if (CollectionsUtil.isNotEmpty(batchKeyList)){
                        param.put("batchKeyIn", batchKeyList);
                        List<ProductStockEntity> warehouseBatchStockList = productStockModel.findEntitys(param);
                        param.remove("batchKeyIn");
                        if (CollectionsUtil.isNotEmpty(warehouseBatchStockList)){
                            warehouseBatchStockList.forEach(item -> {
                                // productId + "_" + warehouseId + "_" + batch + 生产日期 + 保质期
                                batchMap.put(BatchKeyHelp.getBatchKey(item.getProductId() , item.getWarehouseId() , item.getBatch() , item.getProduceDate(),item.getGuaranteePeriod()) ,item.getNum());
                            });
                        }
                    }

                    if (CollectionsUtil.isNotEmpty(warehouseKeyList)){
                        param.put("warehouseKeyIn", warehouseKeyList);
                        List<ProductWarehouseEntity> productWarehouseList = productWarehouseModel.findEntitysWithWarehouseChecked(param, 1);
                        if (CollectionsUtil.isNotEmpty(productWarehouseList)){
                            productWarehouseList.forEach(item -> productWarehouseStockMap.put(item.getWarehouseKey() + "_" + "" + "_" + 0 + "_" + 0,item.getNum()));
                        }
                    }
                    break;
                case OTHER_INSTOCK:
                case PURCHASE_INSTOCK:
                case REFUND_INSTOCK:
                case PRODUCTION_INSTOCK:
                case RETURNED_MATERIEL_INSTOCK:
                    // 新建入库单 数据库没查到的 默认塞0
                    if (CollectionsUtil.isNotEmpty(batchKeyList)){
                        param.put("batchKeyIn", batchKeyList);
                        List<ProductStockEntity> warehouseBatchStockList = productStockModel.findEntitys(param);
                        param.remove("batchKeyIn");
                        if (CollectionsUtil.isNotEmpty(warehouseBatchStockList)){
                            warehouseBatchStockList.forEach(item -> {
                                // productId + "_" + warehouseId + "_" + batch + 生产日期 + 保质期
                                batchMap.put(item.getBatchKey() ,item.getNum());
                            });
                        }
                        // 循环batchKeyList  查处的批次数据没有的时候默认塞0进去
                        for (String batchKey : batchKeyList) {
                            if (!batchMap.containsKey(batchKey)){
                                batchMap.put(batchKey,0D);
                            }
                        }
                    }
                    if (CollectionsUtil.isNotEmpty(warehouseKeyList)){
                        param.put("warehouseKeyIn", warehouseKeyList);
                        List<ProductWarehouseEntity> productWarehouseList = productWarehouseModel.findEntitysWithWarehouseChecked(param, 1);
                        if (CollectionsUtil.isNotEmpty(productWarehouseList)){
                            productWarehouseList.forEach(item -> productWarehouseStockMap.put(item.getWarehouseKey() + "_" + "" + "_" + 0 + "_" + 0,item.getNum()));
                        }
                        for (String warehouseKey : warehouseKeyList) {
                            if (!productWarehouseStockMap.containsKey(warehouseKey + "_" + "" + "_" + 0 + "_" + 0)){
                                productWarehouseStockMap.put(warehouseKey + "_" + "" + "_" + 0 + "_" + 0,0D);
                            }
                        }
                    }
                    break;
            }
            // 将数据库中查询出的数据放入redis查询出的map中 用lua脚本处理
            if (!batchMap.isEmpty()){
                batchMap.keySet().forEach(item -> needAddDataMap.put(item,batchMap.get(item)));
            }
            if (!productWarehouseStockMap.isEmpty()){
                productWarehouseStockMap.keySet().forEach(item ->needAddDataMap.put(item,productWarehouseStockMap.get(item)));
            }
            List<String> kes = new ArrayList<>();
            if (!needAddDataMap.isEmpty()){
                for (Map.Entry<String, Double> stringDoubleEntry : needAddDataMap.entrySet()) {
                    String key = stringDoubleEntry.getKey();
                    Double val = stringDoubleEntry.getValue();
                    Double num = productNumMap.getOrDefault(key,0D);
                    needAddDataList.add(key + "ø/ø" + num + "ø/ø" + val + "ø/ø" );
                    kes.add(key);
                }
            }
            boolean isSuccess = productRedisLock.handleSingleProductStock(RedisPrefixConstant.PRODUCT_STOCK_LOCK,kes, needAddDataList);
            LOG.info("key为:" + needAddDataList.toString() + "是否通过：" + isSuccess);
            if (!isSuccess){
                throw new XbbException(OutstockErrorCodeEnum.API_ERROR_212010);
            }
        }
    }

    // 删除入库单超发出库校验
    public void delInstockCheck(String corpid,List<InstockProductEntity> instockProductEntityExts,Set<Long> disableBatchProducts) throws XbbException {
        Map<String,InstockProductEntity> warehouseKeyMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String,InstockProductEntity> batchKeyMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (InstockProductEntity entity : instockProductEntityExts) {
            entity.setTransferNum(entity.getProductNum());
            if (disableBatchProducts.contains(entity.getParentId())) {
                String key = entity.getProductId() + "_" + entity.getWarehouseId();
                if (warehouseKeyMap.containsKey(key)) {
                    InstockProductEntity instockProductEntity = warehouseKeyMap.get(key);
                    instockProductEntity.setTransferNum(Arith.add(instockProductEntity.getTransferNum(),entity.getTransferNum()));
                    warehouseKeyMap.put(key,instockProductEntity);
                } else {
                    warehouseKeyMap.put(key,entity);
                }
            } else {
                String key = formDataValidateProductHelp.getBatchKey(entity.getProductId(), entity.getWarehouseId(), entity.getBatch(), entity.getProduceDate(), entity.getGuaranteePeriod());
                if (batchKeyMap.containsKey(key)) {
                    InstockProductEntity instockProductEntity = batchKeyMap.get(key);
                    instockProductEntity.setTransferNum(Arith.add(instockProductEntity.getTransferNum(),entity.getTransferNum()));
                    batchKeyMap.put(key,instockProductEntity);
                } else {
                    batchKeyMap.put(key,entity);
                }
            }
        }

        // 去数据库中查询本次删除的入库单的产品数量
        Map<String,Double> dbNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String,Double> delNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<String> redisKeyList = new ArrayList<>();
        Map<String,Object> param = new HashMap<>();
        param.put("corpid",corpid);
        param.put("del",DelEnum.NORMAL.getDel());
        param.put("warehouseChecked",1);
        if (!batchKeyMap.isEmpty()){
            // 将本次删除的数量放入map中
            for (Map.Entry<String, InstockProductEntity> entry : batchKeyMap.entrySet()) {
                String key = entry.getKey();
                Double transferNum = entry.getValue().getTransferNum();
                if (delNumMap.containsKey(key)){
                    delNumMap.put(key,Arith.add(delNumMap.get(key),transferNum));
                }else {
                    delNumMap.put(key,transferNum);
                }
                redisKeyList.add(key);
            }
            param.put("batchKeyIn", batchKeyMap.keySet());
            List<ProductStockEntity> warehouseBatchStockList = productStockModel.findEntitys(param);
            param.remove("batchKeyIn");
            if (CollectionsUtil.isNotEmpty(warehouseBatchStockList)){
                for (ProductStockEntity productStockEntity : warehouseBatchStockList) {
                    String batchKey = productStockEntity.getBatchKey();
                    Double num = productStockEntity.getNum();
                    if (dbNumMap.containsKey(batchKey)){
                        dbNumMap.put(batchKey,Arith.add(delNumMap.get(batchKey),num));
                    }else {
                        dbNumMap.put(batchKey,num);
                    }
                }
            }
        }
        if (!warehouseKeyMap.isEmpty()){
            for (Map.Entry<String, InstockProductEntity> entry : warehouseKeyMap.entrySet()) {
                String key = entry.getKey();
                Double transferNum = entry.getValue().getTransferNum();
                String redisKey = key + "_" + "" + "_" + 0 + "_" + 0;
                if (delNumMap.containsKey(redisKey)){
                    delNumMap.put(redisKey,Arith.add(delNumMap.get(redisKey),transferNum));
                }else {
                    delNumMap.put(redisKey,transferNum);
                }
            }
            param.put("warehouseKeyIn", warehouseKeyMap.keySet());
            List<ProductWarehouseEntity> productWarehouseList = productWarehouseModel.findEntitysWithWarehouseChecked(param, 1);
            if (CollectionsUtil.isNotEmpty(productWarehouseList)){
                for (ProductWarehouseEntity productWarehouseEntity : productWarehouseList) {
                    String warehouseKey = productWarehouseEntity.getWarehouseKey();
                    Double num = productWarehouseEntity.getNum();
                    dbNumMap.put(warehouseKey + "_" + "" + "_" + 0 + "_" + 0,num);
                }
            }
        }
        // 将本次需要添加到redis中的数据通过lua脚本插入redis
        // key + 本次删除数量 + 数据库数量
        List<String> needAddDataList = new ArrayList<>();
        List<String> keys = new ArrayList<>();
        if (!delNumMap.isEmpty()){
            // 删除入库 = 新建出库 所以数量不用* -1 因为lua脚本中是用相减的逻辑
            for (Map.Entry<String, Double> entry : delNumMap.entrySet()) {
                String key = entry.getKey();
                Double delNum = entry.getValue();
                Double dbNum = dbNumMap.getOrDefault(key, 0D);
                needAddDataList.add(key + "ø/ø" + delNum + "ø/ø" + dbNum + "ø/ø");
                keys.add(key);
            }
            boolean isSuccess = productRedisLock.handleSingleProductStock(RedisPrefixConstant.PRODUCT_STOCK_LOCK,keys, needAddDataList);
            LOG.info("key为:" + needAddDataList.toString() + "是否通过：" + isSuccess);
            if (!isSuccess){
                throw new XbbException(InstockErrorCodeEnum.API_ERROR_210038);
            }
        }
    }

    // 删除出库单超发出库校验
    public void delOutstockCheck(String corpid,List<OutstockProductEntity> outstockProductEntityList) throws XbbException {
        List<Long> productIdList = new ArrayList<>();
        outstockProductEntityList.forEach(item ->{productIdList.add(item.getProductId());});
        List<ProductEntityExt> productEntityExts = productModel.getProductListByIdIn(corpid,productIdList,0);
        Set<Long> enableBatchSet = new HashSet<>();
        if (CollectionUtils.isNotEmpty(productEntityExts)){
            for (ProductEntityExt productEntityExt : productEntityExts) {
                Integer enable = productEntityExt.getData().getInteger(ProductEnum.ENABLE_BATCH_SHELF_LIFE.getAttr());
                if (Objects.equals(BasicConstant.ONE,enable)){
                    enableBatchSet.add(productEntityExt.getId());
                }
            }
        }
        Map<String,Double> batchMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String,Double> warehouseMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        List<String> redisKeyList = new ArrayList<>();
        Map<String,Double> delNumMap = new HashMap<>();
        for (OutstockProductEntity outstockProductEntity : outstockProductEntityList) {
            Long productId = outstockProductEntity.getProductId();
            Long warehouseId = outstockProductEntity.getWarehouseId();
            Double productNum = outstockProductEntity.getProductNum();
            String batch = outstockProductEntity.getBatch();
            Long produceDate = outstockProductEntity.getProduceDate();
            Long guaranteePeriod = outstockProductEntity.getGuaranteePeriod();
            String batchKey = BatchKeyHelp.getBatchKey(productId, warehouseId, batch, produceDate, guaranteePeriod);
            redisKeyList.add(batchKey);
            if(enableBatchSet.contains(productId)){
                if (batchMap.containsKey(batchKey)){
                    batchMap.put(batchKey,Arith.add(batchMap.get(batchKey),productNum));
                    delNumMap.put(batchKey,Arith.add(delNumMap.get(batchKey),productNum));
                }else {
                    batchMap.put(batchKey,productNum);
                    delNumMap.put(batchKey,productNum);
                }
            }else {
                String warehouseKey = productId + "_" + warehouseId;
                if (warehouseMap.containsKey(warehouseKey)){
                    warehouseMap.put(warehouseKey,Arith.add(warehouseMap.get(warehouseKey),productNum));
                    delNumMap.put(warehouseKey + "_" + "" + "_" + 0 + "_" + 0,Arith.add(delNumMap.get(warehouseKey + "_" + "" + "_" + 0 + "_" + 0),productNum));
                }else {
                    warehouseMap.put(warehouseKey,productNum);
                    delNumMap.put(warehouseKey + "_" + "" + "_" + 0 + "_" + 0,productNum);
                }
            }
        }
        // 存放数据库库存数量map
        Map<String,Double> dbNumMap = new HashMap<>();
        // 去数据库中查询批次库存及仓库库存
        Map<String,Object> param = new HashMap<>();
        param.put("corpid",corpid);
        param.put("del",DelEnum.NORMAL.getDel());
        param.put("warehouseChecked",1);
        if (!batchMap.isEmpty()){
            param.put("batchKeyIn", batchMap.keySet());
            List<ProductStockEntity> warehouseBatchStockList = productStockModel.findEntitys(param);
            param.remove("batchKeyIn");
            if (CollectionsUtil.isNotEmpty(warehouseBatchStockList)){
                for (ProductStockEntity productStockEntity : warehouseBatchStockList) {
                    String batchKey = productStockEntity.getBatchKey();
                    Double num = productStockEntity.getNum();
                    dbNumMap.put(batchKey,num);
                }
            }
        }
        if(!warehouseMap.isEmpty()){
            param.put("warehouseKeyIn", warehouseMap.keySet());
            List<ProductWarehouseEntity> productWarehouseList = productWarehouseModel.findEntitysWithWarehouseChecked(param, 1);
            if (CollectionsUtil.isNotEmpty(productWarehouseList)){
                for (ProductWarehouseEntity productWarehouseEntity : productWarehouseList) {
                    String warehouseKey = productWarehouseEntity.getWarehouseKey();
                    Double num = productWarehouseEntity.getNum();
                    dbNumMap.put(warehouseKey + "_" + "" + "_" + 0 + "_" + 0,num);
                }
            }
        }
        // 将本次需要添加到redis中的数据通过lua脚本插入redis
        // key + 本次删除数量 + 数据库数量
        List<String> needAddDataList = new ArrayList<>();
        List<String> keys = new ArrayList<>();
        if (!delNumMap.isEmpty()){
            // 删除出库 = 新建入库 所以数量要* -1 因为lua脚本中是用相减的逻辑
            for (Map.Entry<String, Double> entry : delNumMap.entrySet()) {
                String key = entry.getKey();
                Double delNum = entry.getValue() * -1;
                Double dbNum = dbNumMap.getOrDefault(key, 0D);
                needAddDataList.add(key + "ø/ø" + delNum + "ø/ø" + dbNum + "ø/ø");
                keys.add(key);
            }
            boolean isSuccess = productRedisLock.handleSingleProductStock(RedisPrefixConstant.PRODUCT_STOCK_LOCK,keys, needAddDataList);
            LOG.info("key为:" + needAddDataList.toString() + "是否通过：" + isSuccess);
            if (!isSuccess){
                throw new XbbException(InstockErrorCodeEnum.API_ERROR_210038);
            }
        }
    }

    /**
     * 超发出库回滚
     *
     * @param formDataAddDTO
     * @param beforeSaveVO
     */
    public void surpassOutStockRollBack(RollBackStockDTO formDataAddDTO, BeforeSaveVO beforeSaveVO) throws XbbException {
        // 没有审批过的不管
        if (Objects.nonNull(beforeSaveVO) && !Objects.equals(1, beforeSaveVO.getNoApprovalRequired())) {
            return;
        }
        // 单据类型
        Integer businessType = formDataAddDTO.getBusinessType();
        // 如果不是影响库存的这些单据不管
        if (!ProductConstant.EFFECT_PRODUCT_COST.contains(businessType)) {
            return;
        }
        JSONObject data = formDataAddDTO.getDataList();
        // 单据类型枚举
        XbbRefTypeEnum xbbRefTypeEnum = XbbRefTypeEnum.getByCode(businessType);
        Map<String, BigDecimal> rollBackStock = findRollBackStock(data.getJSONArray(InstockEnum.PRODUCT.getAttr()), xbbRefTypeEnum);
        rollBack(rollBackStock);
    }

    /**
     * 删除入库单数据回滚
     *
     * @param productEntityList 入库单产品
     */
    public void deleteInstockStockRollBack(List<InstockProductEntity> productEntityList) {
        Map<String, BigDecimal> rollBackStock = new HashMap<>();
        for (InstockProductEntity productEntity : productEntityList) {
            String batchKey = BatchKeyHelp.getBatchKey(productEntity.getProductId(), productEntity.getWarehouseId(), productEntity.getBatch(), productEntity.getProduceDate(), productEntity.getGuaranteePeriod());
            if (rollBackStock.containsKey(batchKey)) {
                BigDecimal num = rollBackStock.get(batchKey);
                rollBackStock.put(batchKey, num.add(BigDecimal.valueOf(productEntity.getProductNum())));
            } else {
                rollBackStock.put(batchKey, BigDecimal.valueOf(productEntity.getProductNum()));
            }
        }
        rollBack(rollBackStock);
    }

    /**
     * 删除出库单数据回滚
     *
     * @param productEntityList 入库单产品
     */
    public void deleteOutstockStockRollBack(List<OutstockProductEntity>  productEntityList) {
        Map<String, BigDecimal> rollBackStock = new HashMap<>();
        for (OutstockProductEntity productEntity : productEntityList) {
            String batchKey = BatchKeyHelp.getBatchKey(productEntity.getProductId(), productEntity.getWarehouseId(), productEntity.getBatch(), productEntity.getProduceDate(), productEntity.getGuaranteePeriod());
            if (rollBackStock.containsKey(batchKey)) {
                BigDecimal num = rollBackStock.get(batchKey);
                rollBackStock.put(batchKey, num.add(BigDecimal.valueOf(productEntity.getProductNum())));
            } else {
                rollBackStock.put(batchKey, BigDecimal.valueOf(productEntity.getProductNum()));
            }
        }
        rollBack(rollBackStock);
    }

    public void transferOnstockRollBack(Long inWarehouseId, Long outWarehouseId, Map<Object,Double> stockMap, Boolean isInstock, Boolean isOutstock) {
        Map<String, BigDecimal> rollBackStock = new HashMap<>(2);
        stockMap.forEach((key, value) -> {
            String[] batchKeys = String.valueOf(key).split("_");
            StringJoiner instockRedisKey = new StringJoiner("_");
            StringJoiner outstockRedisKey = new StringJoiner("_");
            for (int i = 0; i < batchKeys.length; i++) {
                if (Objects.equals(i, 1)) {
                    instockRedisKey.add(String.valueOf(inWarehouseId));
                    outstockRedisKey.add(String.valueOf(outWarehouseId));
                } else {
                    instockRedisKey.add(batchKeys[i]);
                    outstockRedisKey.add(batchKeys[i]);
                }
            }
            if (isInstock) {
                rollBackStock.put(instockRedisKey.toString(), BigDecimal.valueOf(value).multiply(BigDecimal.valueOf(-1)));
            } else if (isOutstock) {
                rollBackStock.put(outstockRedisKey.toString(), BigDecimal.valueOf(value));
            }
        });
        rollBack(rollBackStock);
    }

    public void InventorytockAndAssembleRollBack(JSONArray instockProductArray, JSONArray outstockProductArray) {
        LOG.info("-------------------- InventorytockAndAssembleRollBack =============================");
        Map<String, BigDecimal> rollBackStock = new HashMap<>(2);
        if (CollectionsUtil.isNotEmpty(instockProductArray)) {
            Map<String, BigDecimal> instockRollBack = findRollBackStock(instockProductArray, XbbRefTypeEnum.INVENTORY);
            instockRollBack.forEach((key , value) -> {
                rollBackStock.put(key, value.multiply(BigDecimal.valueOf(-1)));
            });
        }
        if (CollectionsUtil.isNotEmpty(outstockProductArray)) {
            rollBackStock.putAll(findRollBackStock(outstockProductArray, XbbRefTypeEnum.INVENTORY));
        }
        rollBack(rollBackStock);
    }

    /**
     * 获取需要回滚的库存
     *
     * @param productList    单据产品数据
     * @param xbbRefTypeEnum 单据枚举
     * @return 回滚的库存 key -> redisKye ,value -> 需要回滚库存
     */
    private Map<String, BigDecimal> findRollBackStock(JSONArray productList, XbbRefTypeEnum xbbRefTypeEnum) {
        Map<String, BigDecimal> rollBackStock = new HashMap<>();
        for (int i = 0; i < productList.size(); i++) {
            JSONObject productJson = productList.getJSONObject(i);
            Long warehouseId = this.getWarehouseId(productJson);
            Long getProductId = this.getProductId(productJson);
            Integer integer = productJson.getInteger(ProductEnum.ENABLE_BATCH_SHELF_LIFE.getSaasAttr());
            String batchKey;
            if (Objects.equals(integer, 1)) {
                batchKey = BatchKeyHelp.getBatchKey(getProductId, warehouseId, productJson.getString(SelectProductEnum.BATCH.getAttr()),
                        productJson.getLong(SelectProductEnum.PRODUCE_DATE.getAttr()), getGuaranteePeriod(productJson.getLong(SelectProductEnum.GUARANTEE_PERIOD.getAttr())));
            } else {
                batchKey = BatchKeyHelp.getBatchKey(getProductId, warehouseId, "", 0L, 0L);
            }
            if (rollBackStock.containsKey(batchKey)) {
                BigDecimal num = rollBackStock.get(batchKey);
                rollBackStock.put(batchKey, num.add(this.getNum(productJson, xbbRefTypeEnum)));
            } else {
                rollBackStock.put(batchKey, this.getNum(productJson, xbbRefTypeEnum));
            }


        }
        return rollBackStock;
    }

    /**
     * 数据回滚
     *
     * @param rollBackStock 需要回滚库存的产品信息
     */
    private void rollBack(Map<String, BigDecimal> rollBackStock) {
        if (MapUtils.isEmpty(rollBackStock)) {
            return ;
        }
        rollBackStock.forEach((key, value) -> {
            Boolean hasKey = paasRedisHelper.stockConcurrentLockHashKey(RedisPrefixConstant.PRODUCT_STOCK_LOCK, key);
            if (hasKey) {
                paasRedisHelper.stockConcurrentLockIncrDecimal(RedisPrefixConstant.PRODUCT_STOCK_LOCK, key, value);
            }
        });
    }

    /**
     * 获取产品ID
     *
     * @param productJson 产品json
     * @return 产品ID
     */
    private Long getProductId(JSONObject productJson) {
        long productId;
        Object product = productJson.get(SelectProductEnum.PRODUCT.getAttr());
        if (product instanceof Number || product instanceof String) {
            productId = Long.parseLong(productJson.getString(SelectProductEnum.PRODUCT.getAttr()));
        } else {
            productId = productJson.getJSONObject(SelectProductEnum.PRODUCT.getAttr()).getLong("id");
        }
        return productId;
    }

    /**
     * 获取仓库ID
     *
     * @param productJson 产品json
     * @return 仓库ID
     */
    private Long getWarehouseId(JSONObject productJson) {
        long warehouseId;
        Object warehouse = productJson.get(SelectProductEnum.WAREHOUSE.getAttr());
        if (warehouse instanceof Number || warehouse instanceof String) {
            warehouseId = Long.parseLong(productJson.getString(SelectProductEnum.WAREHOUSE.getAttr()));
        } else {
            warehouseId = productJson.getJSONArray(SelectProductEnum.WAREHOUSE.getAttr()).getJSONObject(0).getLong("id");
        }
        return warehouseId;
    }

    /**
     * 获取出入库数量
     *
     * @param productJson 产品json
     * @return 数量
     */
    private BigDecimal getNum(JSONObject productJson, XbbRefTypeEnum xbbRefTypeEnum) {
        BigDecimal num = productJson.getBigDecimal(SelectProductEnum.NUM.getAttr());
        switch (xbbRefTypeEnum){
            case OUTSTOCK:
            case CONTRACT_OUTSTOCK:
            case RETURNED_PURCHASE_OUTSTOCK:
            case OTHER_OUTSTOCK:
            case PRODUCTION_MATERIEL_OUTSTOCK:
            case WORK_ORDER_OUTSTOCK:
                break;
            case OTHER_INSTOCK:
            case PURCHASE_INSTOCK:
            case REFUND_INSTOCK:
            case PRODUCTION_INSTOCK:
            case RETURNED_MATERIEL_INSTOCK:
                num = num.multiply(BigDecimal.valueOf(-1L));
                break;
        }
        return num;
    }
    
    private Long getGuaranteePeriod(Long guaranteePeriod) {
        if (Objects.isNull(guaranteePeriod)) {
            return 0L;
        }
        return guaranteePeriod * 86400;
    }

    public void checkTransferStock(String corpid,JSONArray productArray,Long intoWarehouseId,Long outWarehouseId,Map<Object,Double> stockMap) throws XbbException{
        // stockMap中Key为批次key或者warehousekey
        if (CollectionUtils.isNotEmpty(productArray)){
            List<String> addDataList = new ArrayList<>();
            List<String> instockBatchKeyList = new ArrayList<>();
            List<String> instockWarehouseKeyList = new ArrayList<>();
            Map<String,Double> addNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<String,Double> inProductNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            Map<String,Double> dbNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            for (int i = 0; i < productArray.size(); i++) {
                JSONObject product = productArray.getJSONObject(i);
                Long productId = product.getLong(TransferProductEnum.PRODUCT.getAttr());
                Double dbNum = 0D;
                String redisKey = "";
                String inRedisKey = "";
                if (!StringUtil.isEmpty(product.getString(SelectProductEnum.BATCH.getAttr()))) {
                    String batch;
                    if (product.get(SelectProductEnum.BATCH.getAttr()) instanceof Map){
                        batch = product.getJSONObject(SelectProductEnum.BATCH.getAttr()).getString(StringConstant.VALUE);
                    }else {
                        batch = product.getString(SelectProductEnum.BATCH.getAttr());
                    }
                    Long guaranteePeriod = product.getLong(SelectProductEnum.GUARANTEE_PERIOD.getAttr()) == null ? 0L : product.getLong(SelectProductEnum.GUARANTEE_PERIOD.getAttr()) * TimeConstant.SECONDS_PER_DAY;
                    redisKey = BatchKeyHelp.getBatchKey(productId, outWarehouseId, batch, product.getLong(SelectProductEnum.PRODUCE_DATE.getAttr()), guaranteePeriod);
                    inRedisKey = BatchKeyHelp.getBatchKey(productId, intoWarehouseId, batch, product.getLong(SelectProductEnum.PRODUCE_DATE.getAttr()), guaranteePeriod);
                    instockBatchKeyList.add(BatchKeyHelp.getBatchKey(productId,intoWarehouseId,batch,product.getLong(SelectProductEnum.PRODUCE_DATE.getAttr()), guaranteePeriod));
                }else {
                    instockWarehouseKeyList.add(productId + "_" + intoWarehouseId);
                    redisKey = BatchKeyHelp.getBatchKey(productId,outWarehouseId,"",0L,0L);
                    inRedisKey = BatchKeyHelp.getBatchKey(productId,intoWarehouseId,"",0L,0L);
                }
                if (stockMap.containsKey(redisKey)){
                    dbNum = stockMap.get(redisKey);
                }
                dbNumMap.put(redisKey,dbNum);
                // 本次入库数量
                Double addNum = product.getDouble(SelectProductEnum.NUM.getAttr());
                if (addNumMap.containsKey(redisKey)){
                    addNumMap.put(redisKey,Arith.add(addNumMap.get(redisKey),addNum));
                }else {
                    addNumMap.put(redisKey,addNum);
                }
                if (inProductNumMap.containsKey(inRedisKey)){
                    inProductNumMap.put(inRedisKey,Arith.add(inProductNumMap.get(inRedisKey),addNum));
                }else {
                    inProductNumMap.put(inRedisKey,addNum);
                }
            }
            List<String> outKeys = new ArrayList<>();
            for (Map.Entry<String, Double> entry : addNumMap.entrySet()) {
                String redisKey = entry.getKey();
                Double addNum = entry.getValue();
                Double dbNum = dbNumMap.getOrDefault(redisKey, 0D);
                addDataList.add(redisKey + "ø/ø" + addNum + "ø/ø" + dbNum + "ø/ø");
                outKeys.add(redisKey);
            }
            boolean isSuccess = productRedisLock.handleSingleProductStock(RedisPrefixConstant.PRODUCT_STOCK_LOCK,outKeys, addDataList);
            LOG.info("key为:" + addDataList.toString() + "是否通过：" + isSuccess);
            if (!isSuccess){
                // 报错库存不足不允许调拨
                throw new XbbException(TransferErrorCodeEnum.API_ERROR_229019);
            }
            // 存放本次入库的产品库存数据
            Map<String,Double> instockStockNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            // 处理入库数据 根据入库batchKeyList和warehouseKeyList查询要入库的库存数量
            Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
            params.put(ParameterConstant.CORPID, corpid);
            params.put("del", DelEnum.NORMAL.getDel());
            if (CollectionUtils.isNotEmpty(instockWarehouseKeyList)){
                params.put("warehouseKeyIn", instockWarehouseKeyList);
                List<ProductWarehouseEntity> productWarehouseEntities = productWarehouseModel.findEntitysWithWarehouseChecked(params, 1);
                params.remove("warehouseKeyIn");
                if (CollectionUtils.isNotEmpty(productWarehouseEntities)){
                    for (ProductWarehouseEntity entity : productWarehouseEntities) {
                        Double num = entity.getNum();
                        String batchKey = BatchKeyHelp.getBatchKey(entity.getProductId(), intoWarehouseId, "", 0L, 0L);
                        if (instockStockNumMap.containsKey(batchKey)){
                            instockStockNumMap.put(batchKey,Arith.add(instockStockNumMap.get(batchKey),num));
                        }else {
                            instockStockNumMap.put(batchKey,num);
                        }
                    }
                }
                // 要入库的没有库存数量的给0
                for (String s : instockWarehouseKeyList) {
                    if (!instockStockNumMap.containsKey(s +"_" + "" + "_" + 0 + "_" + 0)){
                        instockStockNumMap.put(s + "_" + "" + "_" + 0 + "_" + 0,0D);
                    }
                }
            }
            // 查询入库批次库存
            if (CollectionUtils.isNotEmpty(instockBatchKeyList)){
                params.put("batchKeyIn",instockBatchKeyList);
                List<ProductStockEntity> entitys = productStockModel.findEntitys(params);
                if (CollectionUtils.isNotEmpty(entitys)){
                    for (ProductStockEntity entity : entitys) {
                        String batchKey = entity.getBatchKey();
                        Double num = entity.getNum();
                        instockStockNumMap.put(batchKey,num);
                    }
                }
                // 要入库的没有库存数量的给0
                for (String s : instockBatchKeyList) {
                    if (!instockStockNumMap.containsKey(s)){
                        instockStockNumMap.put(s,0D);
                    }
                }
            }
            List<String> keys = new ArrayList<>();
            List<String> addDatas = new ArrayList<>();
            if (!instockStockNumMap.isEmpty()){
                for (Map.Entry<String, Double> entry : instockStockNumMap.entrySet()) {
                    String redisKey = entry.getKey();
                    Double dbNum = entry.getValue();
                    Double addNum = inProductNumMap.getOrDefault(redisKey, 0D) * -1;
                    keys.add(redisKey);
                    addDatas.add(redisKey + "ø/ø" + addNum + "ø/ø" + dbNum +"ø/ø");
                }

            }
            boolean isSuccessful = productRedisLock.handleSingleProductStock(RedisPrefixConstant.PRODUCT_STOCK_LOCK,keys, addDatas);
            LOG.info("key为:" + addDatas.toString() + "是否通过：" + isSuccess);
            if (!isSuccessful){
                // 报错库存不足不允许调拨
                throw new XbbException(TransferErrorCodeEnum.API_ERROR_229019);
            }
        }
    }

    /**
     * @Author: wujian
     * @Description:
     * @Date: 下午2:34 2021/12/16
     * @Param: [outstockProductArray产品数据, warehouseId, corpid, isOutstock是否出库]
     * @return: void
     **/
    public void checkInventory(JSONArray outstockProductArray,Long warehouseId,String corpid ,boolean isOutstock,Integer businessType) throws XbbException{
        List<String> batchKeyList = new ArrayList<>();
        List<String> warehouseKeyList = new ArrayList<>();
        Map<String,Double> addNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        for (int i = 0; i < outstockProductArray.size(); i++) {
            String batchKey = "";
            JSONObject jsonObject = outstockProductArray.getJSONObject(i);
            if (Objects.equals(businessType, XbbRefTypeEnum.ASSEMBLE.getCode())) {
                if (Objects.nonNull(jsonObject.get(SelectProductEnum.WAREHOUSE.getAttr()))) {
                    warehouseId = jsonObject.getLong(SelectProductEnum.WAREHOUSE.getAttr());
                }
            }
            Long productId = jsonObject.getLong(SelectProductEnum.PRODUCT.getAttr());
            String batch = jsonObject.getString(SelectProductEnum.BATCH.getAttr());
            Double num = jsonObject.getDouble(SelectProductEnum.NUM.getAttr());
            Integer enableBatch = jsonObject.getInteger(ProductEnum.ENABLE_BATCH_SHELF_LIFE.getSaasAttr());
            if (Objects.equals(BasicConstant.ONE,enableBatch)){
                // 生产日期
                Long productDate = 0L;
                Long guaranteePeriod = 0L;
                // 开启保质期保质期
                Integer enbaleShelfLifeDays = jsonObject.getInteger(ProductEnum.SHELF_LIFE_DAYS.getSaasAttr());
                if (Objects.equals(BasicConstant.ONE,enbaleShelfLifeDays)){
                    productDate = jsonObject.getLong(SelectProductEnum.PRODUCE_DATE.getAttr());
                    guaranteePeriod = jsonObject.getLong(SelectProductEnum.GUARANTEE_PERIOD.getAttr()) * 86400;
                }
                batchKey = BatchKeyHelp.getBatchKey(productId, warehouseId, batch, productDate, guaranteePeriod);
                batchKeyList.add(batchKey);
            }else {
                String warehouseKey = productId + "_" + warehouseId;
                warehouseKeyList.add(warehouseKey);
                batchKey = warehouseKey + "_"  + "" + "_" + 0 + "_" + 0;
            }
            if (addNumMap.containsKey(batchKey)){
                addNumMap.put(batchKey,Arith.add(addNumMap.get(batchKey),num));
            }else {
                addNumMap.put(batchKey,num);
            }
        }
        // 分别查询出数据库中的库存数量
        Map<String,Double> dbNumMap = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        Map<String, Object> params = new HashMap<>(PaasConstant.DEFAULT_INITIAL_CAPACITY);
        params.put(ParameterConstant.CORPID, corpid);
        params.put("del", DelEnum.NORMAL.getDel());
        if (CollectionUtils.isNotEmpty(warehouseKeyList)){
            params.put("warehouseKeyIn", warehouseKeyList);
            List<ProductWarehouseEntity> productWarehouseEntities = productWarehouseModel.findEntitysWithWarehouseChecked(params, 1);
            params.remove("warehouseKeyIn");
            if (CollectionUtils.isNotEmpty(productWarehouseEntities)){
                for (ProductWarehouseEntity entity : productWarehouseEntities) {
                    Double num = entity.getNum();
                    String batchKey = BatchKeyHelp.getBatchKey(entity.getProductId(), entity.getWarehouseId(), "", 0L, 0L);
                    dbNumMap.put(batchKey,num);
                }
            }
        }
        if (CollectionUtils.isNotEmpty(batchKeyList)){
            params.put("batchKeyIn",batchKeyList);
            List<ProductStockEntity> entitys = productStockModel.findEntitys(params);
            if (CollectionUtils.isNotEmpty(entitys)){
                for (ProductStockEntity entity : entitys) {
                    String batchKey = entity.getBatchKey();
                    Double num = entity.getNum();
                    dbNumMap.put(batchKey,num);
                }
            }
        }
        // 新入库的要补默认0
        if (!isOutstock){
            for (String s : addNumMap.keySet()) {
                if (!dbNumMap.containsKey(s)){
                    dbNumMap.put(s,0D);
                }
            }
        }
        List<String> keys = new ArrayList<>();
        List<String> addDataList = new ArrayList<>();
        if(!addNumMap.isEmpty()){
            for (Map.Entry<String, Double> entry : addNumMap.entrySet()) {
                String batchKey  = entry.getKey();
                Double addNum = entry.getValue();
                if (!isOutstock){
                    addNum = addNum * -1;
                }
                Double dbNum = dbNumMap.getOrDefault(batchKey, 0D);
                keys.add(batchKey);
                addDataList.add(batchKey + "ø/ø" + addNum + "ø/ø" + dbNum +"ø/ø");
            }
            boolean isSuccessful = productRedisLock.handleSingleProductStock(RedisPrefixConstant.PRODUCT_STOCK_LOCK,keys, addDataList);
            LOG.info("key为:" + addDataList.toString() + "是否通过：" + isSuccessful);
            if (!isSuccessful){
                if (Objects.equals(XbbRefTypeEnum.INVENTORY.getCode(),businessType)){
                    throw new XbbException(InventoryErrorCodeEnum.API_ERROR_228022);
                }else {
                    throw new XbbException(AssembleErrorCodeEnum.API_ERROR_231021);
                }
            }
        }
    }
    /**
     * @Author: wujian
     * @Description: 判断是否需要进行超发校验
     * @Date: 下午7:31 2021/12/21
     * @Param: [corpid]
     * @return: boolean  是否校验
     **/
    public boolean checkStock(String corpid){
        FormConfigEntity outStockRuleConfigEntity = formConfigModel.getEntityByBusinessType(corpid, XbbRefTypeEnum.OUTSTOCK.getCode(), FormConfigEnum.OUTSTOCK_INSUFFICIENT_NOT_ALLOW_WAREHOUSING.getConfig());
        //部分公司没有配置表示不开启负库存
        boolean isCheck = Objects.nonNull(outStockRuleConfigEntity) && Objects.equals(outStockRuleConfigEntity.getConfigValue(),"0");
        return !isCheck;
    }
}
